﻿using Microsoft.Extensions.Caching.Memory;
using System.Reflection;

namespace SBlogCore.Common.CacheExtend.MemoryCacheExtension
{
    public class SBlogMemoryCache: ISBlogCoreCache
    {
        private readonly IMemoryCache _memoryCache;
        public SBlogMemoryCache(IMemoryCache memoryCache)
        {
            this._memoryCache = memoryCache;
        }
        /// <summary>
        /// 获取数据
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public TResult? Get<TResult>(string key)
        {
            _memoryCache.TryGetValue(key, out TResult result);
            return result;
        }
        /// <summary>
        /// 存储数据
        /// </summary>
        /// <param name="key"></param>  
        /// <param name="value"></param>
        /// <returns></returns>
        public void Create<TResult>(string key, TResult value)
        {
            using var entry = _memoryCache.CreateEntry(key);
            entry.Value = value;
        }

        /// <summary>
        /// 存储数据
        /// </summary>
        /// <param name="key"></param>  
        /// <param name="value"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        public void Create<TResult>(string key, TResult value, long timeOut)
        {
            using var entry = _memoryCache.CreateEntry(key);
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(timeOut);
            entry.Value = value;
        }
        /// <summary>
        /// 获取或者创建缓存
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="key"></param>
        /// <param name="valueFactory"></param>
        /// <returns></returns>
        public TResult? GetOrCreate<TResult>(string key, Func<TResult>? valueFactory)
        {
            if (_memoryCache.TryGetValue(key, out TResult result) || valueFactory == null) return result;
            result = valueFactory();
            Create(key, result);
            return result;
        }

        /// <summary>
        /// 获取或者创建缓存
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="key"></param>
        /// <param name="valueFactory"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        public TResult? GetOrCreate<TResult>(string key, Func<TResult>? valueFactory, long timeOut)
        {
            if (_memoryCache.TryGetValue(key, out TResult result) || valueFactory == null) return result;
            result = valueFactory();
            Create(key, result, timeOut);
            return result;
        }

        /// <summary>
        /// 异步获取
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public Task<TResult?> GetAsync<TResult>(string key)
        {
            return Task.FromResult(Get<TResult>(key));
        }
        /// <summary>
        /// 异步存储数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Task CreateAsync<TResult>(string key, TResult value)
        {
            Create(key, value);
            return Task.CompletedTask;
        }

        /// <summary>
        /// 异步存储数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        public Task CreateAsync<TResult>(string key, TResult value, long timeOut)
        {
            Create(key, value, timeOut);
            return Task.CompletedTask;
        }

        /// <summary>
        /// 异步获取或者创建缓存
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="key"></param>
        /// <param name="valueFactory"></param>
        /// <returns></returns>
        public async Task<TResult?> GetOrCreateAsync<TResult>(string key, Func<Task<TResult>>? valueFactory)
        {
            if (_memoryCache.TryGetValue(key, out TResult result) || valueFactory == null) return result;
            result =await valueFactory();
            await CreateAsync(key, result);
            return result;
        }
        /// <summary>
        /// 异步获取或者创建缓存
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="key"></param>
        /// <param name="valueFactory"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        public async Task<TResult?> GetOrCreateAsync<TResult>(string key, Func<Task<TResult>>? valueFactory, long timeOut)
        {
            if (_memoryCache.TryGetValue(key, out TResult result) || valueFactory == null) return result;
            result = await valueFactory();
            await CreateAsync(key, result, timeOut);
            return result;
        }

        public void Remove(params string[] cacheKey)
        {
            foreach (var key in cacheKey)
            {
                _memoryCache.Remove(key);
            }
        }

        public Task RemoveAsync(params string[] cacheKey)
        {
            Remove(cacheKey);
            return Task.CompletedTask;
        }

        public async Task<long> RemoveByPatternAsync(string pattern)
        {
            if (string.IsNullOrEmpty(pattern))
                return default;
            var keys = GetAllKeys().Where(k => k.StartsWith(pattern)).ToList();
            if (keys.Any())
                await RemoveAsync(keys.ToArray());
            return keys.Count();
        }
        /// <summary>
        ///获取所有键值
        /// </summary>
        /// <returns></returns>
        private List<string> GetAllKeys()
        {
            var keys = new List<string>();
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var entries = _memoryCache.GetType().GetField("_entries", flags)?.GetValue(_memoryCache);
            if (entries == null) return keys;
            var cacheItems = entries.GetType().GetProperty("Keys")?.GetValue(entries) as ICollection<object>;
            keys = cacheItems?.Select(u => u.ToString()).ToList()!;
            return keys;
        }
    }
}
